#region Copyright (c) 2003-2004 ComponentGo!
/*
*********************************************************************
*                                                                   *
*       ComponentGo! Component Library                              *
*                                                                   *
*       Copyright (c) 2003-2004 ComponentGo!                        *
*       ALL RIGHTS RESERVED                                         *
*                                                                   *
*********************************************************************
*/
#endregion

using System;
using System.Drawing.Drawing2D;
using System.Runtime.Serialization;
using System.Diagnostics;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;

namespace ComponentGo.Calendars {

	/// <summary>
	/// A base class for calendar controls.
	/// </summary>
	/// <remarks>
	/// A class used to build <see cref="DailyCalendar"/>, <see cref="ResourceScheduler"/>, ... controls. 
	/// Delivers funtionality common for these controls and their common properties.
	/// </remarks>
	[System.Runtime.InteropServices.ComVisible(true)]
	public abstract class BaseCalendar : RootCalendar {

		private BreakCollection mBreaks;
		/// <summary>
		/// Calendar breaks.
		/// </summary>
		public BreakCollection Breaks
		{
			set
			{
				mBreaks = value;
			}
			get
			{
				return mBreaks;
			}
		}


		#region General

		internal readonly static DateTime DateZero = new DateTime(1900, 1, 1);
		internal readonly static TimeSpan OneDay = new TimeSpan(1, 0, 0, 0);

		internal BaseCalendar(RootCalendar root, bool isHScroll, bool isVScroll) : base(root) {
			SetStyle(ControlStyles.Opaque, true);
			SetStyle(ControlStyles.AllPaintingInWmPaint, true);
			SetStyle(ControlStyles.DoubleBuffer, true);
			SetStyle(ControlStyles.UserPaint, true);
			SetStyle(ControlStyles.ResizeRedraw, true);
			SetStyle(ControlStyles.Selectable, true);
			SetStyle(ControlStyles.UserMouse, true);

			if (root==null) {
				this.Appointments.calendar = this;
				this.Resources.calendar = this;
			}

			if (isVScroll) {
				vScroll = new VScrollBar();
				vScroll.Scroll += new ScrollEventHandler(vScroll_Scroll);
				Controls.Add(vScroll);
			}
			if (isHScroll) {
				hScroll = new HScrollBar();
				hScroll.Scroll += new ScrollEventHandler(hScroll_Scroll);
				Controls.Add(hScroll);
			}
			mBreaks = new BreakCollection(); 
		}
		
		internal abstract void initScrollBars();

		internal enum AppRegion { None, Content, Marker, BeginMargin, EndMargin }

		#endregion

		#region Scrollbars

		readonly internal VScrollBar vScroll;
		readonly internal HScrollBar hScroll;

		internal virtual void vScroll_Scroll(object sender, ScrollEventArgs args) {}
		internal abstract void hScroll_Scroll(object sender, ScrollEventArgs args);

		public void ResizeScrollbars()
		{
			resizeScrollbars();
		}
		
		internal void resizeScrollbars() 
		{
			if (!Visible) return;

			int w = vScroll==null || IsPrinting ? 0 : VerticalScrollBarWidth;
			int h = hScroll==null || IsPrinting ? 0 : HorizontalScrollBarHeight;
			if (h>0)
				hScroll.SetBounds(0, InternalHeight-h, InternalWidth-w, h);
			
			if (w>0)
				vScroll.SetBounds(InternalWidth-w, 0, w, InternalHeight-h);

			initScrollBars();
		}

		internal void paintScrollbarSpace(Graphics g) {
			if (ScrollBars==ScrollBars.Both)
				using (Brush brush = new SolidBrush(BackColor))
					g.FillRectangle(brush, 
						Width-SystemInformation.VerticalScrollBarWidth, Height-SystemInformation.HorizontalScrollBarHeight,
						SystemInformation.VerticalScrollBarWidth, SystemInformation.HorizontalScrollBarHeight);
		}

		ScrollBars scrollBars = ScrollBars.Both;

		/// <summary>
		/// Gets or sets which scroll bars should appear calendar control.
		/// </summary>
		/// <value>
		/// On of the ScrollBars enumeration values that indicates whether a Calendar control 
		/// appears with no scroll bars, a horizontal scroll bar, a vertical scroll bar, 
		/// or both. The default is <b>ScrollBars.Both</b>.
		/// </value>
		[DefaultValue(ScrollBars.Both)]
		[Description("Which scroll bars should appear calendar control.")]
		[Category("Appearance")]
		public ScrollBars ScrollBars {
			get 
			{ 
				return IsPrinting ? ScrollBars.None : scrollBars; 
			}
			set 
			{
				if (scrollBars==value) return;
				scrollBars = value;
				switch (ScrollBars) {
					case ScrollBars.Both:
						if (vScroll!=null) vScroll.Visible = true;
						if (hScroll!=null) hScroll.Visible = true;
						break;
					case ScrollBars.Horizontal:
						if (vScroll!=null) vScroll.Visible = false;
						if (hScroll!=null) hScroll.Visible = true;
						break;
					case ScrollBars.Vertical:
						if (vScroll!=null) vScroll.Visible = true;
						if (hScroll!=null) hScroll.Visible = false;
						break;
					case ScrollBars.None:
						if (vScroll!=null) vScroll.Visible = false;
						if (hScroll!=null) hScroll.Visible = false;
						break;
				}
				Invalidate();
				resizeScrollbars();
			}
		}

		internal int VerticalScrollBarWidth 
		{
			get 
			{
				if (!IsPrinting && (scrollBars==ScrollBars.Both || scrollBars==ScrollBars.Vertical))
					return SystemInformation.VerticalScrollBarWidth;
				return 0;
			}
		}

		internal int HorizontalScrollBarHeight 
		{
			get 
			{
				if (!IsPrinting && (scrollBars==ScrollBars.Both || scrollBars==ScrollBars.Horizontal))
					return SystemInformation.HorizontalScrollBarHeight;
				return 0;
			}
		}

		#endregion

		#region Overrides

		/// <summary>
		/// Overloaded. Implementation of a calendar.
		/// </summary>
		/// <param name="e">Like overloaded</param>
		protected override void OnResize(System.EventArgs e) 
		{
			base.OnResize(e);
			resizeScrollbars();
			if (!stopEdit(false))
				stopEdit(true);
			Resync(true);
		}

		/// <summary>
		/// Overloaded. Implementation of a calendar.
		/// </summary>
		/// <param name="e">Like overloaded</param>
		protected override void OnVisibleChanged(EventArgs e) {
			base.OnVisibleChanged(e);
			resizeScrollbars();
			Resync(true);
		}

		/// <summary>
		/// Overrided. Implementation of a calendar control.
		/// </summary>
		/// <param name="e">Like overloaded</param>
		protected override void OnValidating(CancelEventArgs e) 
		{
			if (!stopEdit(false))
				e.Cancel = true;
			else
				base.OnValidating(e);
		}

		#endregion

		#region Colors properties

		#region WorkHour & FreeHour Colors

		Color workHourColor = Color.LemonChiffon;
		Color extraBreakHourColor = Color.Fuchsia; 

		/// <summary>
		/// Background color for workdays (Monday to Friday, 8:00...16:00).
		/// </summary>
		/// <remarks>
		/// Working hours are set with <see cref="WorkHourBegin"/>  
		/// and  <see cref="WorkHourEnd"/> properties. 
		/// Workdays are set with a <see cref="FreeDays"/> property.
		/// </remarks>
		[Category("Colors")]
		[Description("Background color for workdays (Monday to Friday, 8:00...16:00)")]
		public Color WorkHourColor {
			get { return workHourColor; }
			set {
				if (workHourColor==value) return;
				if (CalendarTools.Dark(workHourColor)==workHourColorSecond)
					workHourColorSecond = CalendarTools.Dark(value);
				workHourColor = value;
				Invalidate();
			}
		}

		/// <summary>
		/// Indicates whether the <see cref="WorkHourColor"/> property should be persisted.
		/// </summary>
		/// <returns><b>true</b> if the property value has changed from its 
		/// default; otherwise, <b>false</b>.</returns>
		/// <remarks>You typically use this method if you are creating a designer
		/// for <see cref="BaseCalendar"/>.
		/// </remarks>
		public bool ShouldSerializeWorkHourColor() {
			return workHourColor!=Color.LemonChiffon;
		}

		Color workHourColorSecond = CalendarTools.Dark(Color.LemonChiffon);

		/// <summary>
		/// Second color for a <see cref="WorkHourColor"/> property used 
		/// for a linear gradient
		/// </summary>
		[Category("Colors")]
		[Description("Background color for workdays (Monday to Friday, 8:00...16:00)")]
		public Color WorkHourColorSecond {
			get { return workHourColorSecond; }
			set {
				if (workHourColorSecond==value) return;
				workHourColorSecond = value;
				Invalidate();
			}
		}

		/// <summary>
		/// Indicates whether the <see cref="WorkHourColorSecond"/> property should be persisted.
		/// </summary>
		/// <returns><b>true</b> if the property value has changed from its 
		/// default; otherwise, <b>false</b>.</returns>
		/// <remarks>You typically use this method if you are creating a designer
		/// for <see cref="BaseCalendar"/>.
		/// </remarks>
		public bool ShouldSerializeWorkHourColorSecond() {
			return workHourColorSecond!=CalendarTools.Dark(workHourColor);
		}

		/// <summary>
		/// Sets a default value of a property <see cref="WorkHourColorSecond"/>.
		/// </summary>
		/// <remarks>You typically use this method if you are creating a designer
		/// for <see cref="BaseCalendar"/>.
		/// </remarks>
		public void ResetWorkHourColorSecond() {
			WorkHourColorSecond = CalendarTools.Dark(workHourColor);
		}

		Color freeHourColor = Color.FromArgb(255,238,158) ; //.DarkKhaki;

		/// <summary>
		/// Background color for free hours, outside workdays.
		/// </summary>
		/// <remarks>
		/// Working hours are set with <see cref="WorkHourBegin"/>  
		/// and  <see cref="WorkHourEnd"/> properties. 
		/// Workdays are set with a <see cref="FreeDays"/> property.
		/// </remarks>
		[Category("Colors")]
		[Description("Background color for free hours, outside workdays.")]
		public Color FreeHourColor {
			get { return freeHourColor; }
			set {
				if (freeHourColor==value) return;
				freeHourColor = value;
				Invalidate();
			}
		}

		[Category("Colors")]
		[Description("Background color for free hours, outside workdays.")]
		public Color ExtraBreakHourColor 
		{
			get { return extraBreakHourColor; }
			set 
			{
				if (extraBreakHourColor==value) return;
				extraBreakHourColor = value;
				Invalidate();
			}
		}

		/// <summary>
		/// Indicates whether the <see cref="FreeHourColor"/> property should be persisted.
		/// </summary>
		/// <returns><b>true</b> if the property value has changed from its 
		/// default; otherwise, <b>false</b>.
		/// You typically use this method if you are creating a designer
		/// for <see cref="DailyCalendar"/>.
		/// </returns>
		public bool ShouldSerializeFreeHourColor() 
		{
			return freeHourColor!=Color.DarkKhaki;
		}

		#endregion

		#region LineColor

		Color lineColor = SystemColors.ControlDark;
		Color dayLineColor = Color.FromArgb(215,177,0); //SystemColors.ControlDark;

		/// <summary>
		/// Color of day grid, lines separating the hours etc.
		/// </summary>
		[Category("Colors")]
		[Description("Color of day grid, lines separating the hours etc.")]
		public Color LineColor {
			get { return lineColor; }
			set {
				if (lineColor==value) return;
				lineColor = value;
				Invalidate();
			}
		}
		public Color DayLineColor 
		{
			get { return dayLineColor; }
			set 
			{
				if (dayLineColor==value) return;
				dayLineColor = value;
				Invalidate();
			}
		}
		/// <summary>
		/// Indicates whether the <see cref="LineColor"/> property should be persisted.
		/// </summary>
		/// <returns><b>true</b> if the property value has changed from its 
		/// default; otherwise, <b>false</b>.</returns>
		/// <remarks>You typically use this method if you are creating a designer
		/// for <see cref="BaseCalendar"/>.
		/// </remarks>
		public bool ShouldSerializeLineColor() {
			return lineColor!=SystemColors.ControlDark;
		}

		#endregion

		#region SelectedColor

		Color selectedColor = SystemColors.Highlight;

		/// <summary>
		/// Selected hours color.
		/// </summary>
		/// <remarks>
		/// Hours are selected by means of a <see cref="SelectedDateBegin"/> 
		/// and <see cref="SelectedDateEnd"/> properties.
		/// </remarks>
		[Category("Colors")]
		[Description("Selected hours color")]
		public Color SelectedColor {
			get { return selectedColor; }
			set {
				if (selectedColor==value) return;
				selectedColor = value;
				Invalidate();
			}
		}

		/// <summary>
		/// Indicates whether the <see cref="SelectedColor"/> property should be persisted.
		/// </summary>
		/// <returns><b>true</b> if the property value has changed from its 
		/// default; otherwise, <b>false</b>.</returns>
		/// <remarks>You typically use this method if you are creating a designer
		/// for <see cref="BaseCalendar"/>.
		/// </remarks>
		public bool ShouldSerializeSelectedColor() {
			return selectedColor!=SystemColors.Highlight;
		}

		#endregion

		#region SelectedAppointmentColor

		Color selectedAppointmentColor = SystemColors.HighlightText;

		/// <summary>
		/// Selected appointments color.
		/// </summary>
		[Category("Colors")]
		[Description("Selected appointments color.")]
		public Color SelectedAppointmentColor {
			get { return selectedAppointmentColor; }
			set {
				if (selectedAppointmentColor==value) return;
				selectedAppointmentColor = value;
				Invalidate();
			}
		}

		/// <summary>
		/// Indicates whether the <see cref="SelectedAppointmentColor"/> property should be persisted.
		/// </summary>
		/// <returns><b>true</b> if the property value has changed from its 
		/// default; otherwise, <b>false</b>.</returns>
		/// <remarks>You typically use this method if you are creating a designer
		/// for <see cref="BaseCalendar"/>.
		/// </remarks>
		public bool ShouldSerializeSelectedAppointmentColor() 
		{
			return selectedAppointmentColor!=SystemColors.HighlightText;
		}


		#region SelectedAppointmentTextColor

		Color selectedAppointmentTextColor = SystemColors.HighlightText;

		/// <summary>
		/// Selected appointments text color.
		/// </summary>
		[Category("Colors")]
		[Description("Selected appointments text color.")]
		public Color SelectedAppointmentTextColor 
		{
			get { return selectedAppointmentTextColor; }
			set 
			{
				if (selectedAppointmentTextColor==value) return;
				selectedAppointmentTextColor = value;
				Invalidate();
			}
		}

		/// <summary>
		/// Indicates whether the <see cref="SelectedAppointmentTextColor"/> property should be persisted.
		/// </summary>
		/// <returns><b>true</b> if the property value has changed from its 
		/// default; otherwise, <b>false</b>.</returns>
		/// <remarks>You typically use this method if you are creating a designer
		/// for <see cref="TabularCalendar"/>.
		/// </remarks>
		public bool ShouldSerializeSelectedAppointmentTextColor() 
		{
			return SelectedAppointmentTextColor!=SystemColors.HighlightText;
		}

		#endregion
		
		#endregion

		#region TodayColor

		Color todayMarkerColor = Color.DarkOrange;

		/// <summary> 
		/// Today and current time marker color. 
		/// </summary> 
		/// <remarks> 
		/// A today's and current date and time marker is displayed on a time axis. 
		/// This color is used to display a marker.<para/> 
		/// A marker display is controlled by a <see cref="TodayMarkerVisible"/> property.  
		/// </remarks> 
		[Category("Colors")]
		[Description("The today and current time marker color.")]
		public Color TodayMarkerColor {
			get { return todayMarkerColor; }
			set {
				if (todayMarkerColor==value) return;
				todayMarkerColor = value;
				Invalidate();
			}
		}

		/// <summary>
		/// Indicates whether the <see cref="TodayMarkerColor"/> property should be persisted.
		/// </summary>
		/// <returns><b>true</b> if the property value has changed from its 
		/// default; otherwise, <b>false</b>.</returns>
		/// <remarks>You typically use this method if you are creating a designer
		/// for <see cref="BaseCalendar"/>.
		/// </remarks>
		public bool ShouldSerializeTodayMarkerColor() {
			return TodayMarkerColor!=Color.DarkOrange;
		}

		#endregion

		#endregion

		#region Hide properties

		/// <summary>
		/// Overrided. Implementation of a calendar control.
		/// </summary>
		[Category("Layout")]
		[Description("Gets or sets the coordinates of the upper-left corner of the control relative to the upper-left corner of its container.")]
		public new Point Location {
			get { return base.Location; }
			set { base.Location = value; }
		}

		/// <summary>
		/// Overrided. Implementation of a calendar control.
		/// </summary>
		/// <returns>Like overrided method.</returns>
		public bool ShouldSerializeLocation() {
			return shouldSerialize();
		}

		/// <summary>
		/// Overrided. Implementation of a calendar control.
		/// </summary>
		[Category("Design")]
		[Description("Gets or sets the name of the control.")]
		public new string Name {
			get { return base.Name; }
			set { base.Name = value; }
		}

		/// <summary>
		/// Overrided. Implementation of a calendar control.
		/// </summary>
		/// <returns>Like overrided method.</returns>
		public bool ShouldSerializeName() {
			return shouldSerialize();
		}

		/// <summary>
		/// Overrided. Implementation of a calendar control.
		/// </summary>
		[Category("Layout")]
		[Description("Gets or sets the height and width of the control.")]
		public new Size Size {
			get { return base.Size; }
			set { base.Size = value; }
		}

		/// <summary>
		/// Overrided. Implementation of a calendar control.
		/// </summary>
		/// <returns>Like overrided method.</returns>
		public bool ShouldSerializeSize() {
			return shouldSerialize();
		}

		/// <summary>
		/// Overrided. Implementation of a calendar control.
		/// </summary>
		[Category("Behavior")]
		[Description("Gets or sets the tab order of the control within its container.")]
		public new int TabIndex {
			get { return base.TabIndex; }
			set { base.TabIndex = value; }
		}

		/// <summary>
		/// Overrided. Implementation of a calendar control.
		/// </summary>
		/// <returns>Like overrided method.</returns>
		public bool ShouldSerializeTabIndex() {
			return shouldSerialize();
		}

		/// <summary>
		/// Overrided. Implementation of a calendar control.
		/// </summary>
		[Category("Behavior")]
		[Description("Gets or sets a value that indicates whether a server control is rendered as UI on the page.")]
		public new bool Visible {
			get { return base.Visible; }
			set { base.Visible = value; }
		}

		/// <summary>
		/// Overrided. Implementation of a calendar control.
		/// </summary>
		/// <returns>Like overrided method.</returns>
		public bool ShouldSerializeVisible() {
			return shouldSerialize() && !Visible;
		}

		#endregion

		#region Appearance properties

		HoursResolutions hoursResolution = HoursResolutions.Half;

		/// <summary>
		/// Sets the hour resolution, that is the resolution the hours are displayed with.
		/// </summary>
		[DefaultValue(HoursResolutions.Half)]
		[Category("Appearance")]
		[Description("Sets the hour resolution, that is the resolution the hours are displayed with.")]
		public HoursResolutions HoursResolution {
			get { return hoursResolution; }
			set {
				if (value==hoursResolution) return;
				hoursResolution = value;
				OnHoursResolutionChanged(EventArgs.Empty);
				initScrollBars();
				Resync(true);
			}
		}

		/// <summary>
		/// An event raized after the <see cref="HoursResolution"/> property change.
		/// </summary>
		[Category("Property Changed")]
		[Description("An event raized after the HoursResolution property change")]
		public event EventHandler HoursResolutionChanged;

		/// <summary>
		///	Raises the <see cref="HoursResolutionChanged"/> event.
		/// </summary>
		/// <param name="args">An <see cref="EventArgs"/> that contains the event data.</param>
		protected virtual void OnHoursResolutionChanged(EventArgs args) {
			if (HoursResolutionChanged!=null)
				HoursResolutionChanged(this, args);
		}

		FreeDays freeDay = FreeDays.Weekend;

		/// <summary>
		/// Determines which days are holidays.
		/// </summary>
		/// <remarks>
		/// Holidays are painted with a <see cref="FreeHourColor"/> color.
		/// Workdays are painted with a <see cref="WorkHourColor"/> color.
		/// </remarks>
		[DefaultValue(FreeDays.Weekend)]
		[Category("Appearance")]
		[Description("Determines which days are holidays (do not contain working hours).")]
		public FreeDays FreeDay {
			get { return freeDay; }
			set { 
				if (freeDay==value) return;
				freeDay = value; 
				Invalidate();
			}
		}

		bool todayMarkerVisible = true;

		/// <summary> 
		/// Determines whether today's and current time marker should be visible. 
		/// </summary> 
		/// <remarks> 
		/// A marker is displayed using <see cref="TodayMarkerColor"/> color. 
		/// </remarks> 
		[DefaultValue(true)]
		[Category("Appearance")]
		[Description("Determines whether today's and current time marker should be visible.")]
		public bool TodayMarkerVisible {
			get { return todayMarkerVisible; }
			set { 
				if (todayMarkerVisible==value) return;
				todayMarkerVisible = value; 
				Invalidate();
			}
		}

		#endregion

		#region Behavior properties

		static TimeSpan defaultHourBegin = new TimeSpan(8, 0, 0);
		TimeSpan workHourBegin = defaultHourBegin;

		/// <summary>
		/// The first working hour within a day.
		/// </summary>
		/// <remarks>
		/// Working hours are displayed in a calendar with a  <see cref="WorkHourColor"/> color.
		/// <seealso cref="WorkHourEnd"/>
		/// <seealso cref="FreeDay"/>
		/// </remarks>
		/// <example>
		/// The example shows how to set working hours from 6:00 to 14:00.
		/// <code>
		/// dailyCalendar.WorkHourBegin = new TimeSpan(6, 0, 0);
		/// dailyCalendar.WorkHourEnd = new TimeSpan(14, 0, 0);
		/// </code>
		/// </example>
		[Category("Behavior")]
		[Description("The first working hour within a day")]
		public TimeSpan WorkHourBegin {
			get { return workHourBegin; }
			set {
				value = round(value, false);
				if (workHourBegin==value) return;
				workHourBegin = value;
				initScrollBars();
				Invalidate();
			}
		}

		/// <summary>
		/// Indicates whether the <see cref="WorkHourBegin"/> property should be persisted.
		/// </summary>
		/// <returns><b>true</b> if the property value has changed from its 
		/// default; otherwise, <b>false</b>.</returns>
		/// <remarks>You typically use this method if you are creating a designer
		/// for <see cref="BaseCalendar"/>.
		/// </remarks>
		public bool ShouldSerializeWorkHourBegin() {
			return WorkHourBegin!=defaultHourBegin;
		}

		static TimeSpan defaultHourEnd = new TimeSpan(16, 0, 0);
		TimeSpan workHourEnd = defaultHourEnd;

		/// <summary>
		/// The last working hour within a day.
		/// </summary>
		/// <remarks>
		/// Working hours are displayed in a calendar with a  <see cref="WorkHourColor"/> color.
		/// <seealso cref="WorkHourBegin"/>
		/// <seealso cref="FreeDay"/>
		/// </remarks>
		/// <example>
		/// The example shows how to set working hours from 6:00 to 14:00.
		/// <code>
		/// dailyCalendar.WorkHourBegin = new TimeSpan(6, 0, 0);
		/// dailyCalendar.WorkHourEnd = new TimeSpan(14, 0, 0);
		/// </code>
		/// </example>
		[Category("Behavior")]
		[Description("The last working hour within a day")]
		public TimeSpan WorkHourEnd {
			get { return workHourEnd; }
			set {
				value = round(value, false);
				if (workHourEnd==value) return;
				workHourEnd = value;
				Invalidate();
			}
		}

		/// <summary>
		/// Indicates whether the <see cref="WorkHourEnd"/> property should be persisted.
		/// </summary>
		/// <returns><b>true</b> if the property value has changed from its 
		/// default; otherwise, <b>false</b>.</returns>
		/// <remarks>You typically use this method if you are creating a designer
		/// for <see cref="BaseCalendar"/>.
		/// </remarks>
		public bool ShouldSerializeWorkHourEnd() {
			return WorkHourEnd!=defaultHourEnd;
		}

		#endregion

		#region Appointments property

		internal abstract RequestAppointmentsEventArgs CreateRequestAppointmentsArgs();

		internal RequestAppointmentsEventArgs forceAppointments() {
			RequestAppointmentsEventArgs args = CreateRequestAppointmentsArgs();
			OnRequestAppointments(args);
			return args;
		}

		#endregion

		#region Resources property

		internal bool isValidResource(int resourceIdx, bool zeroValid) {
			return zeroValid && Resources.Count==0 && resourceIdx==0 
				|| 0<=resourceIdx && resourceIdx<Resources.Count;
		}

		internal int ResCount {
			get { return Math.Max(Resources.Count, 1); }
		}
		
		#endregion

		#region Implementation

		public abstract void Resync(bool recalc);

		public void ReloadAppointments()
		{
			Resync(true);
			Invalidate(true);
		}


		internal TimeSpan minsResolution {
			get {
				return CalendarTools.CalcMinsResolution(HoursResolution);
			}
		}

		internal TimeSpan round(TimeSpan ts, bool up) {
			int secs = (int)minsResolution.TotalSeconds;
			int v = up ? secs-1 : 0;
			return TimeSpan.FromSeconds((((int)ts.TotalSeconds+v)/secs)*secs);
		}

		internal DateTime round(DateTime dt, bool up) {
			int secs = (int)minsResolution.TotalSeconds;
			int v = up ? secs-1 : 0;
			return dt.Date.AddSeconds((((int)dt.TimeOfDay.TotalSeconds+v)/secs)*secs);
		}

		internal static bool intersected(DateTime afrom, DateTime ato, DateTime bfrom, DateTime bto) {
			return afrom<bto && bfrom<ato;
		}

		internal static bool intersected(DateTime aform, DateTime ato, Appointment ap) {
			return intersected(aform, ato, ap.dateStart, ap.dateStop);
		}

		internal static bool intersected(Appointment ap1, Appointment ap2) {
			return intersected(ap1.dateStart, ap1.dateStop, ap2.dateStart, ap2.dateStop);
		}

		internal bool roundIntersected(Appointment ap1, Appointment ap2) {
			return intersected(
				round(ap1.dateStart, false), round(ap1.dateStop, true), 
				round(ap2.dateStart, false), round(ap2.dateStop, true));
		}

		#endregion

		#region Selected

		#region Begin & End

		DateTime selectedDateBegin = DateTime.MinValue;
		DateTime selectedDateEnd = DateTime.MinValue;
		bool growEnd = true;
		
		/// <summary>
		/// Date and hour of a beginning of a selected time period.
		/// </summary>
		/// <remarks>
		/// Selected time period is painted on a calendar with a special color:
		/// <see cref="SelectedColor"/>.<para/>
		/// Setting this property results in a <see cref="RootCalendar.SelectionChanged"/> event 
		/// being generated.
		/// </remarks>
		/// <example>
		/// The example shows how to select a whole today's day:
		/// <code>
		/// calendar.SelectedDateBegin = DateTime.Today;
		/// calendar.SelectedDateEnd = DateTime.Today.AddDays(1);
		/// </code>
		/// <seealso cref="SelectedDateEnd"/>
		/// </example>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		[Browsable(false)]
		public DateTime SelectedDateBegin {
			get { return selectedDateBegin; }
			set {
				if (selectedDateBegin==value) return;
				selectedDateBegin = value;
				OnSelectionChanged(AppointmentEventArgs.Empty);
				Invalidate();
			}
		}

		/// <summary>
		/// Date and hour of the end of a selected time period
		/// </summary>
		/// <remarks>
		/// Selected time period is painted on a calendar with a special color:
		/// <see cref="SelectedColor"/>.<para/>
		/// Setting this property results in a <see cref="RootCalendar.SelectionChanged"/> event 
		/// being generated.
		/// </remarks>
		/// <example>
		/// The example shows how to select a whole today's day:
		/// <code>
		/// calendar.SelectedDateBegin = DateTime.Today;
		/// calendar.SelectedDateEnd = DateTime.Today.AddDays(1);
		/// </code>
		/// <seealso cref="SelectedDateBegin"/>
		/// </example>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		[Browsable(false)]
		public DateTime SelectedDateEnd {
			get { return selectedDateEnd; }
			set {
				if (selectedDateEnd==value) return;
				selectedDateEnd = value;
				OnSelectionChanged(AppointmentEventArgs.Empty);
				Invalidate();
			}
		}

		internal int selectedResource = 0;
		
		/// <summary>
		/// Index of a selected <see cref="Resource"/> object.
		/// </summary>
		/// <remarks>
		/// Selected resource is painted on a calendar with a special color:
		/// <see cref="SelectedColor"/>.<para/>
		/// Setting this property results in a <see cref="RootCalendar.SelectionChanged"/> event 
		/// being generated.
		/// </remarks>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		[Browsable(false)]
		public int SelectedResource {
			get { return selectedResource; }
			set {
				if (selectedResource==value) return;
				if (!isValidResource(value, false))
					throw new ArgumentOutOfRangeException("value");
				selectedResource = value;
				OnSelectionChanged(AppointmentEventArgs.Empty);
				Invalidate();
			}
		}

		internal virtual void updateSelection(DateTime dateBegin, DateTime dateEnd, int resource, bool growEnd) {
			if (resource>=Resources.Count) resource = Resources.Count-1;
			if (resource<0) resource = 0;
			
			if (selectedDateBegin!=dateBegin || selectedDateEnd!=dateEnd || selectedResource!=resource) {
				selectedDateBegin = dateBegin;
				selectedDateEnd = dateEnd;
				selectedResource = resource;
				OnSelectionChanged(AppointmentEventArgs.Empty);
				Invalidate();
			}
			this.growEnd = growEnd;
			EnsureSelectionVisible();
		}

		#endregion

		#region EnsureVisible

		/// <summary>
		/// Forces visibility of a given period in a calendar.
		/// </summary>
		/// <param name="dateFrom">A beginning of a period, that is to be visible in a calendar.</param>
		/// <param name="dateTo">An end of a period, that is to be visible in a calendar.</param>
		/// <param name="resourceIdx">A index of resource, that is to be visible in a calendar.</param>
		/// <remarks>
		/// It is possible that a given period wil not be wholly visible, because a calendar
		/// is too small. In this case a largest possible portion of a period will be visible.
		/// </remarks>
		public abstract void EnsureVisible(DateTime dateFrom, DateTime dateTo, int resourceIdx);

		/// <summary>
		/// Forces visibility of a given day and an hour in a calendar.
		/// </summary>
		/// <param name="date">Day to be visible.</param>
		public void EnsureVisible(DateTime date) {
			EnsureVisible(date, date, SelectedResource);
		}

		/// <summary>
		/// Forces visibility of a given resource with specified index.
		/// </summary>
		/// <param name="resourceIdx">Index of resource to be visible.</param>
		public void EnsureVisible(int resourceIdx) {
			EnsureVisible(SelectedDateBegin, SelectedDateEnd, resourceIdx);
		}

		/// <summary>
		/// Forces visibility of selected data on a calendar.
		/// </summary>
		public void EnsureSelectionVisible() {
			EnsureVisible(SelectedDateBegin, SelectedDateEnd, SelectedResource);
		}

		#endregion

		#region Move

		void moveBy(TimeSpan ts, TimeSpan step, int resourceIdx) {
			DateTime dt = ts>TimeSpan.Zero ? SelectedDateEnd+ts-step : SelectedDateBegin+ts;
			updateSelection(dt, dt+step, resourceIdx, true);
		}

		void moveTo(DateTime dt, TimeSpan step, int resourceIdx) {
			updateSelection(dt, dt+step, resourceIdx, true);
		}

		void growBy(TimeSpan ts, int resourceIdx) {
			DateTime b = SelectedDateBegin, e = SelectedDateEnd;
			bool g = growEnd;
			if (growEnd)
				e += ts;
			else
				b += ts;
			if (b>=e) {
				g = !g;
				if (ts<TimeSpan.Zero) ts = -ts;
				b -= ts;
				e += ts;
			}
			updateSelection(b, e, resourceIdx, g);
		}

		void growTo(DateTime dt, TimeSpan step, int resourceIdx) {
			DateTime b = SelectedDateBegin, e = SelectedDateEnd;
			bool g = growEnd;
			if (growEnd)
				e = dt+step;
			else 
				b = dt;
			if (b>=e) {
				g = !g;
				dt = e;
				e = b+step;
				b = dt-step;
			}
			updateSelection(b, e, resourceIdx, g);
		}

		internal void moveCursor(bool grow, TimeSpan ts, TimeSpan cursor, int resourceIdx) {
			if (grow)
				growBy(ts, resourceIdx);
			else
				moveBy(ts, cursor, resourceIdx);
		}

		internal void moveCursor(bool grow, DateTime dt, TimeSpan cursor, int resourceIdx) {
			if (grow)
				growTo(dt, cursor, resourceIdx);
			else
				moveTo(dt, cursor, resourceIdx);
		}

		internal abstract void checkSelected();

		#endregion

		#region Visible Begin & End

		internal abstract DateTime VisibleFirstDateTime { get; set; }
		internal abstract TimeSpan VisibleTimeSpan { get; set; }

		#endregion

		#endregion

		#region Paint appointment

		internal void InvokePaintAppointment(PaintAppointmentEventArgs args) {
			OnPaintAppointment(args);
			if (!args.UserPaint)
				Paint(args.Graphics, args.Appointment);
		}

		/// <summary>
		/// Draws an appointment in a selected graphics object.
		/// </summary>
		/// <param name="graphics">Graphics object used to display an appointment.</param>
		/// <param name="ap">An appointment drawn in a calendar. </param>
		public new abstract void Paint(Graphics graphics, Appointment ap);
			
		#endregion

		#region Rectangles & Hits

		/// <summary>
		/// Calculates a rectangle, containing text located inside an appointment's rectangle.
		/// </summary>
		/// <remarks>
		///	Rectangle of appointment's text is also used as a rectangle of a text editor used to edit the text.
		/// </remarks>
		/// <param name="ap">Appointment a rectangle is calculated for.</param>
		/// <param name="eip">Determines whether a rectangle used by EiP <see cref="EditControl"/> should be calculated.</param>
		/// <returns>A rectangle calculated.</returns>
		public abstract Rectangle GetAppointmentTextRectangle(Appointment ap, bool eip);
			
		/// <summary>
		/// Check which appointment object is located at given coordinates
		/// </summary>
		/// <param name="pt">Coordinates relative to a calendar control
		/// the appointment is searched for.</param>
		/// <returns>The object of an <see cref="Appointment"/> type located at given 
		/// coordinates or <b>null</b> in case there is no appointment at given coordinates.
		/// </returns>
		public virtual Appointment HitAppointmentTest(Point pt) {
			foreach (Appointment ap in Appointments)
				foreach (Rectangle rect in GetAppointmentRectangles(ap))
					if (rect.Contains(pt))
						return ap;
			return null;
		}

		/// <summary>
		/// Calculates appointment's rectangle array, changing all parts.
		/// </summary>
		/// <remarks>
		///	An array containing all rectangles contituting a displayed part of an appointment.
		///	Parts that are not displayed (outside a currently displayed part of a calendar) 
		///	are not placed in an array.<para/>
		///	This array can also contain <see cref="Rectangle.Empty"/> values.
		/// </remarks>
		/// <param name="ap">Appointment a rectangle is calculated for.</param>
		/// <returns>A calculated array containing rectangles.</returns>
		public abstract Rectangle[] GetAppointmentRectangles(Appointment ap);
		
		/// <summary>
		/// Calculates an index of a resource located at a given point.
		/// </summary>
		/// <remarks>
		/// If a <b>pt</b> point is located in the first resource, the method returns -1. 
		/// If a <b>pt</b> point is located below the last resource, the method returns <b>Resources.Count</b>.
		/// <seealso cref="BaseCollection.Count"/>
		/// </remarks>
		/// <param name="pt">Coordinates relative to a calendar control, 
		///		an resource is calculated for.</param>
		/// <returns>A calculated index of a resource.</returns>
		public abstract int HitResourceTest(Point pt);
		
		/// <summary>
		/// Calculates a day and an hour located at given coordinates.
		/// </summary>
		/// <param name="pt">
		/// Coordinates relative to a calendar control, an datetime is calculated for.
		/// </param>
		/// <returns>Structure containing calculated information on day and hour.</returns>
		public abstract DateTime HitDateTimeTest(Point pt);

		internal abstract AppRegion HitAppointmentRegion(Appointment ap, Point pt);
		
		#endregion

		#region Mouse

		#region DragDropData

		class DragDropData {
			
			readonly Appointment Base;
			readonly TimeSpan Delta;
			readonly DateTime dateZero;
			readonly DateTime saveBegin;
			readonly bool saveAllDay;
			readonly int saveResource;
			readonly int thisIdx;
			Appointment [] Changes;
			public bool Valid = true;
			public readonly bool Outer;

			public DragDropData(Appointment ap, TimeSpan delta, bool outer) {
				this.Base = ap;
				this.Changes = null;
				this.Changes = new Appointment[ap.Linked.Length]; 
				for(int i=0; i< ap.Linked.Length; i++ )
					this.Changes[i] = (Appointment)ap.Linked[i];
				this.Delta = delta;
				this.Outer = outer;

				this.dateZero = Changes[0].DateBegin;
				this.thisIdx = Array.IndexOf(Changes, Base);

				this.saveBegin = ap.DateBegin;
				this.saveAllDay = ap.AllDay;
				this.saveResource = ap.ResourceIndex;
			}

			public Appointment Appointment {
				get {
					return Changes[thisIdx];
				}
			}

			public DateTime GetBegin(DateTime dt) {
				return dt-Delta;
			}

			public DateTime GetEnd(DateTime dt) {
				Appointment ap = Appointment;
				return dt-Delta+(ap.DateEnd-ap.DateBegin);
			}

			public void Clone(AppointmentCollection aps) {
				if (Base!=Changes[thisIdx]) return;
				Appointment [] t = new Appointment[Changes.Length];
				for (int i = t.Length; --i>=0; )
					t[i] = Changes[i].Clone();
				aps.AddRange(t);
				Restore();
				Changes = t;
			}

			public void UndoClone(AppointmentCollection aps) {
				if (Base==Changes[thisIdx]) return;
				foreach (Appointment ap in Changes)
					aps.Remove(ap);
				this.Changes = null;
				this.Changes = new Appointment[Base.Linked.Length]; 
				for(int i=0; i< Base.Linked.Length; i++ )
					this.Changes[i] = (Appointment)Base.Linked[i];

			}

			public void Restore() {
				Move(saveAllDay ? saveBegin : saveBegin+Delta, saveAllDay);
				Move(saveResource);
			}

			public void Move(DateTime dt, bool allDay) {
				if (!allDay)
					dt -= Delta;
				dt += dateZero-saveBegin;
				foreach (Appointment ap in Changes) {
					TimeSpan len = ap.DateEnd-ap.DateBegin;
					ap.Move(dt, dt+len, allDay);
					dt += len;
					if (allDay)
						dt = dt.AddDays(1);
				}
			}

			public void Move(int resourceIndex) {
				Changes[thisIdx].ResourceIndex = resourceIndex;
			}

			public bool SetValid(bool valid) {
				if (Valid==valid) return false;
				Valid = valid;
				foreach (Appointment ap in Changes)
					ap.isGhost = !Valid;
				return true;
			}
		}

		DragDropData dddAp = null;

		#endregion

		#region GrowAppointment

		internal class GrowAppointment {
			public readonly Appointment Appointment;
			readonly DateTime Start;
			readonly BaseCalendar calendar;
			public readonly bool Begin;
			public bool Valid = true;
			
			public GrowAppointment(BaseCalendar calendar, Appointment ap, bool begin) {
				this.calendar = calendar;
				this.Appointment = ap;
				this.Begin = begin;
				this.Start = begin ? ap.DateBegin : ap.DateEnd;
			}

			public void Restore() {
				GrowTo(Start, TimeSpan.Zero);
			}

			public void GrowTo(DateTime dt, TimeSpan step) {
				if (Begin) {
					if (Appointment.DateEnd>dt) {
						Appointment apl = (Appointment)Appointment.PreviousLinked;
						if (apl!=null) {
							if (dt<apl.DateBegin+step)
								dt = apl.DateBegin+step;
							apl.DateEnd = dt;
						}

						if (calendar.GrowMode==GrowMode.ChangeLinkTime)
							Appointment.DateBegin = dt;
						else {
							Appointment [] aps = (Appointment[])Appointment.Linked;
							for (int i = Array.IndexOf(aps, Appointment); i<aps.Length; ++i) {
								apl = aps[i];
								TimeSpan ts = apl.DateEnd-apl.DateBegin;
								apl.Move(dt, dt+ts, apl.AllDay);
								dt = apl.DateEnd;
							}
						}
					}
				}
				else {
					dt += step;
					if (Appointment.DateBegin<dt) {
						if (calendar.GrowMode==GrowMode.ChangeLinkTime) {
							Appointment apl = (Appointment)Appointment.NextLinked;
							if (apl!=null) {
								if (dt>apl.DateEnd-step)
									dt = apl.DateEnd-step;
								apl.DateBegin = dt;
							}
						}
						else {
							DateTime dt2 = dt;
							Appointment [] aps = (Appointment[])Appointment.Linked;
							for (int i = Array.IndexOf(aps, Appointment)+1; i<aps.Length; ++i) {
								Appointment apl = aps[i];
								TimeSpan ts = apl.DateEnd-apl.DateBegin;
								apl.Move(dt2, dt2+ts, apl.AllDay);
								dt2 = apl.DateEnd;
							}
						}
						Appointment.DateEnd = dt;
					}
				}
			}

			public bool SetValid(bool valid) {
				if (valid==Valid) return false;
				Valid = valid;
				foreach (Appointment ap in Appointment.Linked)
					ap.isGhost = !Valid;
				return true;
			}
		}

		internal GrowAppointment growAp = null;

		#endregion

		Appointment canEip = null;

		internal abstract void mouseClick(MouseEventArgs e, bool grow);
		internal abstract Cursor getCursorAppearance(Point pt, Appointment ap);
		internal virtual bool recalcPointOnDrag(ref Point pt) { return true; }

		internal virtual bool recalcAllDayOnDrag(Point pt, Appointment ap) { 
			return ap.AllDay; 
		}

		#region QueryAppointmentDrag

		/// <summary>
		/// Event that checks, whether a drag&amp;drop operation can be performed with parameters supplied.
		/// </summary>
		/// <remarks>
		/// Handling this event you can lock a date and time setting onto a given value.<para/>
		/// Properties of an event argument class contains information on an appointment object 
		/// passed accourding to time and resource properties already set. 
		/// The confirmation of these parameters validity is done by appropriately setting 
		/// the property <see cref="QueryAppointmentDragEventArgs.Valid"/>. 
		/// In case setting time and resource is incorrect, after a drag&amp;drop operation data 
		/// will data will be restored to previous values.
		/// <seealso cref="RootCalendar.AllowOverlap"/>
		/// <seealso cref="Resource.ForceEmpty"/>
		/// </remarks>
		[Category("Drag Drop")]
		[Description("Checks correctness of a drag&drop operation")]
		public event QueryAppointmentDragEventHandler QueryAppointmentDrag;

		/// <summary>
		///	Raises the <see cref="QueryAppointmentDrag"/> event.
		/// </summary>
		/// <param name="args">An <see cref="EventArgs"/> that contains the event data.</param>
		protected virtual void OnQueryAppointmentDrag(QueryAppointmentDragEventArgs args) {
			if (QueryAppointmentDrag!=null)
				QueryAppointmentDrag(this, args);
		}

		/// <summary>
		/// Checks whether an appointment is assigned a correct time.
		/// </summary>
		/// <remarks>
		/// Checking consists in calling a <see cref="QueryAppointmentDrag"/> event and checking 
		/// whether an appontment is not in the wrong resource (<see cref="Resource.ForceEmpty"/>) 
		/// and when the <see cref="RootCalendar.AllowOverlap"/> property is set, whether it does not overlap 
		/// any other appointment.
		/// </remarks>
		/// <param name="ap">An appointment checked.</param>
		/// <returns><b>true</b> in case a Drag&amp;Drop operation can be completed correctly. </returns>
		public bool CheckValidDrop(Appointment ap) {
			if (!isValidResource(ap.ResourceIndex, true)) return false;
			if (Resources.Count!=0 && Resources[ap.ResourceIndex].ForceEmpty) return false;
			if (!AllowOverlap)
				foreach (Appointment ap1 in ap.Linked)
					foreach (Appointment ap2 in Appointments)
						if (ap1!=ap2 && 
							(Resources.Count==0 || ap1.ResourceIndex==ap2.ResourceIndex) && 
							intersected(ap1, ap2))
							return false;
			return true;
		}

		#endregion

		#region Overrided events

		/// <summary>
		/// Overloaded. Implementation of a calendar.
		/// </summary>
		/// <param name="e">Like overloaded</param>
		protected override void OnMouseDown(MouseEventArgs e) {
			base.OnMouseDown(e);

			Point pt = new Point(e.X, e.Y);

			if (e.Button==MouseButtons.Left && stopEdit(false)) {
				/////////////////////////////////////////////////////////////
				// Select appointment
				// 
				Appointment ap = (Appointment)HitAppointmentTest(pt);
				if (ap!=null) {
					AppRegion reg = HitAppointmentRegion(ap, pt);
					if (reg==AppRegion.Content)
						canEip = ap;
					else
						canEip = null;

					if ((ModifierKeys & (Keys.Control | Keys.Shift))==0) {
						Appointments.UnselectAll();
						ap.Selected = true;
					}
					else
						ap.Selected = !ap.Selected;
					SelectedDateEnd = SelectedDateBegin;

					StartAppointmentDragEventArgs sadargs;
					switch (reg) {
						case AppRegion.Content: 
						case AppRegion.Marker: {
							DateTime dt = HitDateTimeTest(pt);
							if (SplitMode && ap.dateStart<dt && dt<ap.dateStop) {
								DateTime dt2 = ap.AllDay ? dt.Date : dt;
								SplitAppointmentEventArgs args = new SplitAppointmentEventArgs(ap, dt2);
								InvokeSplitAppointment(args);
								ap = args.DragAppointment;
							}

							if (ap!=null) {
								sadargs = new StartAppointmentDragEventArgs(ap);
								OnStartAppointmentDrag(sadargs);
								if(!sadargs.Cancel)
								{
									DragDropData ddd = new DragDropData(ap, dt-ap.dateStart, false);
									DataObject data = new DataObject();
									data.SetData(ddd);
									data.SetData(ap);
									DoDragDrop(data, DragDropEffects.Copy | DragDropEffects.Move);
								}
							}
							break;
						}
						case AppRegion.BeginMargin:
							sadargs = new StartAppointmentDragEventArgs(ap);
							OnStartAppointmentDrag(sadargs);
							if(!sadargs.Cancel)
							{
								growAp = new GrowAppointment(this, ap, true);
								DoDragDrop(growAp, DragDropEffects.All);
							}

							break;
						case AppRegion.EndMargin: 
							sadargs = new StartAppointmentDragEventArgs(ap);
							OnStartAppointmentDrag(sadargs);
							if(!sadargs.Cancel)
							{
								growAp = new GrowAppointment(this, ap, false);
								DoDragDrop(growAp, DragDropEffects.All);
							}
							break;
					}
					if (e.Clicks>1)
						OnDefault(EventArgs.Empty);
				}
				else  {
					checkSelected();
					mouseClick(e, (ModifierKeys & Keys.Shift)!=0);
//					if (e.Clicks>1)
//						OnDefault(EventArgs.Empty);
				}
			}
		}

		/// <summary>
		/// Overloaded. Implementation of a calendar.
		/// </summary>
		/// <param name="e">Like overloaded</param>
		protected override void OnMouseMove(System.Windows.Forms.MouseEventArgs e) {
			base.OnMouseMove(e);

			//////////////////////////////////////////////////
			// Cursor appearance change
			// 
			Point pt = new Point(e.X, e.Y);
			Appointment ap = (Appointment)HitAppointmentTest(pt);
			if (ap==null) {
				Cursor = Cursors.Default;
				if (e.Button==MouseButtons.Left) {
					checkSelected();
					mouseClick(e, true);
				}
			}
			else {
				switch (HitAppointmentRegion(ap, pt)) {
					case AppRegion.Marker: Cursor = Cursors.SizeAll; break;
					case AppRegion.BeginMargin:
					case AppRegion.EndMargin: 
						Cursor = getCursorAppearance(pt, ap); 
						break;
					default: 
						if (SplitMode)
							Cursor = Cursors.Cross;
						else
							Cursor = Cursors.Default; 
						break;
				}
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="e"></param>
		protected override void OnMouseWheel(System.Windows.Forms.MouseEventArgs e) 
		{
			base.OnMouseWheel(e);
			
			ScrollBar sbar = null;

			if (vScroll!=null) 
				sbar = vScroll;
			else 
				sbar = hScroll;

			if (sbar!=null) 
			{
				if (!sbar.Enabled) return;

				if (e.Delta>0)
				{
					if(sbar.Value - sbar.SmallChange  < sbar.Minimum )
						sbar.Value = sbar.Minimum;
					else
						sbar.Value -= sbar.SmallChange;
				}
				else
				{
					if(sbar.Value + sbar.SmallChange >= sbar.Maximum )
						sbar.Value = sbar.Maximum-1;
					else
						sbar.Value += sbar.SmallChange;
				}

				try
				{
					if(sbar == vScroll)
						vScroll_Scroll(this, new ScrollEventArgs(ScrollEventType.ThumbPosition, vScroll.Value));
					else
						hScroll_Scroll(this, new ScrollEventArgs(ScrollEventType.ThumbPosition, hScroll.Value));
					
					int v = sbar.Value-sbar.Minimum;
					int w = sbar.Maximum-sbar.Minimum;
					bool change = false;
					if (v<w/10) change = true;
					if (v+sbar.LargeChange>w-w/10) change = true;
					if (change)
						initScrollBars();
				}
				catch
				{
				}
				//initScrollBars();
			}
		}


		#endregion

		#region Drag & Drop

		/// <summary>
		/// Overloaded. Implementation of a calendar.
		/// </summary>
		/// <param name="gfbevent">Like overloaded</param>
		protected override void OnGiveFeedback(System.Windows.Forms.GiveFeedbackEventArgs gfbevent) 
		{
			base.OnGiveFeedback(gfbevent);
			if (growAp!=null) {
				gfbevent.UseDefaultCursors = false;
				if (!growAp.Valid)
					Cursor = Cursors.No;
				else 
					Cursor = getCursorAppearance(Point.Empty, growAp.Appointment);
			}
			else if (dddAp!=null && !dddAp.Valid) {
				gfbevent.UseDefaultCursors = false;
				Cursor = Cursors.No;
			}
		}

		/// <summary>
		/// Overloaded. Implementation of a calendar.
		/// </summary>
		/// <param name="qcdevent">Like overloaded</param>
		protected override void OnQueryContinueDrag(System.Windows.Forms.QueryContinueDragEventArgs qcdevent) {
			base.OnQueryContinueDrag(qcdevent);
			if (qcdevent.EscapePressed ||
				(MouseButtons & MouseButtons.Left)!=0 && (MouseButtons & MouseButtons.Right)!=0)
				qcdevent.Action = DragAction.Cancel;
		}

		/// <summary>
		/// Overloaded. Implementation of a calendar.
		/// </summary>
		/// <param name="drgevent">Like overloaded</param>
		protected override void OnDragEnter(System.Windows.Forms.DragEventArgs drgevent) {
			base.OnDragEnter(drgevent);
			//Appointment a =  (Appointment)drgevent.Data.GetData(typeof(Appointment));
			
			growAp = (GrowAppointment)drgevent.Data.GetData(typeof(GrowAppointment));
			dddAp = (DragDropData)drgevent.Data.GetData(typeof(DragDropData));
			if (dddAp==null && growAp==null) 
			{
				//Appointment ap = (Appointment)drgevent.Data.GetData(typeof(Appointment));
				Appointment a = (Appointment)drgevent.Data.GetData(typeof(Appointment)); 
				if (a!=null)
					dddAp = new DragDropData(a, TimeSpan.Zero, true);
			}
		}

		/// <summary>
		/// Overloaded. Implementation of a calendar.
		/// </summary>
		/// <param name="drgevent">Like overloaded</param>
		protected override void OnDragOver(System.Windows.Forms.DragEventArgs drgevent) {
			//return;
			base.OnDragOver(drgevent);
			Point pt = new Point(drgevent.X, drgevent.Y);
			pt = PointToClient(pt);
			Point rpt = pt;
			bool valid = recalcPointOnDrag(ref rpt);

			if (growAp!=null) {
				DateTime dt = HitDateTimeTest(rpt);
				if (dt!=DateTime.MinValue) {
					drgevent.Effect = drgevent.AllowedEffect;
					growAp.GrowTo(dt, minsResolution);
				
					if (valid) {
						QueryAppointmentDragEventArgs args = new QueryAppointmentDragEventArgs(
							growAp.Appointment, CheckValidDrop(growAp.Appointment), false);
						OnQueryAppointmentDrag(args);
						valid = args.Valid;
					}

					if (growAp.SetValid(valid))
						Invalidate();
				}
			}
			else if (dddAp!=null) {
				canEip = null;
				drgevent.Effect = (drgevent.KeyState & 8)!=0 || dddAp.Outer ? 
					DragDropEffects.Copy : DragDropEffects.Move;
				bool isCopy = drgevent.Effect==DragDropEffects.Copy;
				if (isCopy)
					dddAp.Clone(Appointments);
				else
					dddAp.UndoClone(Appointments);

				DateTime dt = HitDateTimeTest(pt);
				if (dt!=DateTime.MinValue)
					dddAp.Move(dt, recalcAllDayOnDrag(pt, dddAp.Appointment));

				int resourceIdx = HitResourceTest(pt);
				if (Resources.Count!=0 && isValidResource(resourceIdx, false))
					dddAp.Move(resourceIdx);

				if (valid) {
					QueryAppointmentDragEventArgs args = new QueryAppointmentDragEventArgs(
						dddAp.Appointment, CheckValidDrop(dddAp.Appointment), isCopy);
					OnQueryAppointmentDrag(args);
					valid = args.Valid;
				}

				if (dddAp.SetValid(valid))
					Invalidate();
			}
		}

		/// <summary>
		/// Overloaded. Implementation of a calendar.
		/// </summary>
		/// <param name="drgevent">Like overloaded</param>
		protected override void OnDragDrop(DragEventArgs drgevent) {
			base.OnDragDrop(drgevent);

			if (growAp!=null) {
				if (growAp.SetValid(true))
					growAp.Restore();
				else {
					OnAppointmentChanged(new AppointmentEventArgs(growAp.Appointment));
					merge(growAp.Appointment, growAp.Begin);
				}
			}
			if (dddAp!=null) {
				if (dddAp.SetValid(true)) {
					dddAp.UndoClone(Appointments);
					dddAp.Restore();
				}
				else {
					OnAppointmentChanged(new AppointmentEventArgs(dddAp.Appointment));
					merge(dddAp.Appointment, true);
					merge(dddAp.Appointment, false);
				}
			}
			dddAp = null;
			growAp = null;
		}


		/// <summary>
		/// Overloaded. Implementation of a calendar.
		/// </summary>
		/// <param name="e">Like overloaded</param>
		protected override void OnDragLeave(System.EventArgs e) {
			base.OnDragLeave(e);
			if (growAp!=null)
				growAp.Restore();
			if (dddAp!=null) {
				dddAp.UndoClone(Appointments);
				dddAp.Restore();

				if (EnableEditInPlace && canEip!=null)
					startEdit(canEip);
			}
			growAp = null;
			dddAp = null;
		}

		#endregion

		#endregion

		#region Keyboard

		/// <summary>
		/// Overloaded. Implementation of a calendar.
		/// </summary>
		/// <param name="keyData">Overloaded.</param>
		/// <returns>Like overloaded.</returns>
		/// <remarks>
		/// Handling of arrow and Shift+arrow keys.
		/// </remarks>
		protected override bool IsInputKey(Keys keyData) {
			switch (keyData) {
				case Keys.Left:
				case Keys.Right:
				case Keys.Up:
				case Keys.Down:
				case Keys.Shift | Keys.Left:
				case Keys.Shift | Keys.Right:
				case Keys.Shift | Keys.Up:
				case Keys.Shift | Keys.Down:
					return true;
			}
			return base.IsInputKey(keyData);
		}

		/// <summary>
		/// Overloaded. Implementation of a calendar.
		/// </summary>
		/// <param name="keyData">Overloaded.</param>
		/// <returns>Like overloaded.</returns>
		/// <remarks>
		/// Handling of Enter, Esc, Tab and Shift+Tab keys.
		/// </remarks>
		protected override bool ProcessDialogKey(System.Windows.Forms.Keys keyData) {
			if (editAppointment!=null) {
				switch (keyData) {
					case Keys.Enter: 
						stopEdit(false);
						return true;
					case Keys.Escape: 
						stopEdit(true); 
						return true;
				}
			}
			switch (keyData) {
				case Keys.Enter:
					OnDefault(EventArgs.Empty);
					break;
				case Keys.Tab:
					if (selectNext()) return true;
					break;
				case Keys.Shift | Keys.Tab: 
					if (selectPrevious()) return true;
					break;
			}
			return base.ProcessDialogKey(keyData);
		}

		/// <summary>
		/// Overloaded. Implementation of a calendar.
		/// </summary>
		/// <param name="e">Like overloaded</param>
		protected override void OnKeyDown(KeyEventArgs e) {
			checkSelected();
			if (processKey(e)) {
				EnsureSelectionVisible();
				e.Handled = true; 
			}
			base.OnKeyDown(e);
		}

		internal virtual bool processKey(KeyEventArgs e) {
			return false;
		}

		bool selectNext() {
			Appointment ap = editAppointment;
			if (ap!=null && !stopEdit(false)) return true;
			ap = getNext(ap);
			if (ap==null) return false;
			ap.Edit();
			return true;
		}

		bool selectPrevious() {
			Appointment ap = editAppointment;
			if (ap==null) return false;
			if (!stopEdit(false)) return true;
			ap = getPrevious(ap);
			if (ap==null)
				Select();
			else
				ap.Edit();
			return true;
		}

		/// <summary>
		/// Overrided. Implementation of a calendar control.
		/// </summary>
		/// <param name="directed">See overided.</param>
		/// <param name="forward">See overided.</param>
		/// <remarks>
		/// Handles navigation with Tab and Shift+Tab keys.
		/// </remarks>
		protected override void Select(bool directed, bool forward) {
			if (directed) {
				if (forward)
					Select();
				else {
					Appointment ap = getPrevious(null);
					if (ap!=null)
						ap.Edit();
					else
						Select();
				}
			}
			else
				base.Select(directed, forward);
		}

		internal abstract Appointment getNext(Appointment ap);
		internal abstract Appointment getPrevious(Appointment ap);

		#endregion

		#region EIP

		Control editControl = null;
		internal Appointment editAppointment = null;

		/// <summary>
		/// Sets the control used to edit an appointment object.
		/// </summary>
		/// <remarks>
		/// As a standard, a <see cref="TextBox"/> control editing a
		/// <see cref="Appointment.Text"/> property of an <see cref="Appointment"/> object
		/// is used. 
		/// It is possible to implement your own edit control using 
		/// an <see cref="RootCalendar.Editing"/> event.<para/>
		/// A value of <b>null</b> means, that no object is being edited at the moment.
		/// </remarks>
		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public Control EditControl {
			get { return editControl; }
		}

		/// <summary>
		/// Returns an appointment in a calendar being edited.
		/// </summary>
		/// <remarks>
		/// If it contains a value that is not <b>null</b>, it is and  <see cref="Appointment"/>
		/// object in a calendar being edited.
		/// <para/>
		/// Otherwise a value of  <b>null</b> means that no object is being edited.
		/// <seealso cref="RootCalendar.EnableEditInPlace"/>
		/// <seealso cref="Appointment.Edit"/>
		/// </remarks>
		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public Appointment EditAppointment {
			get { return editAppointment; }
		}

		internal void startEdit(Appointment app) {
			if (editControl!=null) {
				if (!stopEdit(false))
					return;
			}

			EditAppointmentEventArgs args = new EditAppointmentEventArgs(app);
			OnEditing(args);
			if (args.Cancel) return;
			
			editControl = args.Control;
			if (editControl==null) {
				TextBox tb = new TextBox();
				tb.Multiline = true;
				tb.ForeColor = app.GetForeColor();
				tb.BackColor = app.GetBackColor();
				tb.Font = app.Font;
				tb.BorderStyle = BorderStyle.None;
				editControl = tb;
			}
			if (args.Control==null)
				editControl.Text = app.Text;

			editControl.Bounds = GetAppointmentTextRectangle(app, true);
			Controls.Add(editControl);
			editControl.Select();
			editAppointment = app;
		}

		internal bool stopEdit(bool cancel) {
			if (editControl==null) return true;

			EditAppointmentEventArgs args = new EditAppointmentEventArgs(editAppointment);
			args.Control = editControl;
			args.Cancel = cancel;
			OnEdited(args);
			if (args.ContinueEdit) return false;
		
			if (args.Control!=null) {
				if (!args.Cancel)
					editAppointment.Text = args.Control.Text;

				Controls.Remove(args.Control);
				args.Control.Dispose();
			}

			OnAppointmentChanged(new AppointmentEventArgs(EditAppointment));

			editControl = null;
			editAppointment = null;

			return true;
		}

		#endregion

		#region Split

		bool splitMode = false;

		/// <summary>
		/// <b>true</b> means a split mode (appointment splitting) is activated.
		/// </summary>
		/// <remarks>
		/// A split mode will be automatically turned off after a appointment is splitted. 
		/// During a split operation, an event <see cref="RootCalendar.SplitAppointment"/> is called , 
		/// and can be used to define the way an appointment is split.
		/// </remarks>
		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public bool SplitMode {
			get { return splitMode; }
			set { splitMode = value; }
		}

		internal void InvokeSplitAppointment(SplitAppointmentEventArgs args) {
			SplitMode = false;

			OnSplitAppointment(args);

			if (!args.Handled) {
				if (!(args.Appointment.dateStart<args.SplitTime && args.SplitTime<args.Appointment.dateStop))
					throw new InvalidOperationException("Invalid split time");

				DateTime endDt = args.SplitTime;
				if (args.Appointment.AllDay)
					endDt = endDt.AddDays(-1);

				Appointment newAp = ((Appointment)args.Appointment).Clone();
				args.Appointment.DateEnd = endDt;
				args.Appointment.LinkWithNext = true;
				newAp.DateBegin = args.SplitTime;
				Appointments.Insert(Appointments.IndexOf(args.Appointment)+1, newAp);

				args.DragAppointment = newAp;

				OnAppointmentChanged(new AppointmentEventArgs(args.Appointment));
				OnAppointmentChanged(new AppointmentEventArgs(newAp));
			}
		}

		#endregion

		#region Merge

		void merge(Appointment ap1, bool before) {
			Appointment [] linked = ap1.Linked;
			MergeAppointmentsEventArgs args = null;

			foreach (Appointment ap2 in Appointments) {
				if (ap1.ResourceIndex==ap2.ResourceIndex && ap1!=ap2 &&
					(before ? ap1.dateStart==ap2.dateStop : ap1.dateStop==ap2.dateStart)) {
					args = new MergeAppointmentsEventArgs(ap1, ap2, Array.IndexOf(linked, ap2)<0);
					if (!args.Cancel)
						break;
				}
			}
			
			if (args!=null)
				InvokeMergeAppointments(args);
		}

		/// <summary>
		///	Raises the <see cref="RootCalendar.MergeAppointments"/> event.
		/// </summary>
		/// <param name="args">An <see cref="MergeAppointmentsEventArgs"/> that contains the event data.</param>
		internal void InvokeMergeAppointments(MergeAppointmentsEventArgs args) {
			OnMergeAppointments(args);

			int idx = Appointments.IndexOf(args.SecondAppointment);
			if (!args.Cancel && idx>=0) {
				bool save = idx>0 ? Appointments[idx-1].LinkWithNext : false;

				if (args.Appointment.dateStop==args.SecondAppointment.dateStart)
					args.Appointment.DateEnd = args.SecondAppointment.DateEnd;
				else
					args.Appointment.DateBegin = args.SecondAppointment.DateBegin;

				Appointments.Remove(args.SecondAppointment);
				if (save)
					Appointments[idx-1].LinkWithNext = true;

				OnAppointmentChanged(new AppointmentEventArgs(args.Appointment));
				OnAppointmentChanged(new AppointmentEventArgs(args.SecondAppointment));
			}
		}

		#endregion

		#region Print

		class PrintInfo 
		{
			readonly Rectangle rt;

			public PrintInfo(Rectangle rt) 
			{
				this.rt = rt;
			}

			public int Width 
			{ 
				get { return rt.Width; }
			}

			public int Height 
			{ 
				get { return rt.Height; }
			}
		}

		PrintInfo printInfo = null;

		/// <summary>
		/// Determines, whether a calendar is currently being printed.
		/// </summary>
		/// <value>A value of  <b>true</b> means that a callendar is in print mode.
		/// Some properties including size were changed for the time 
		/// a calendar is being printed. A value of <b>false</b> means normal 
		/// operation  of a control in a window.</value>
		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public bool IsPrinting 
		{
			get { return printInfo!=null; }
		}

		internal int InternalWidth 
		{
			get 
			{ 
				return printInfo==null ? Width : printInfo.Width; 
			}
		}

		internal int InternalHeight 
		{
			get 
			{
				return printInfo==null ? Height : printInfo.Height; 
			}
		}

		/// <summary>
		/// Allows to print a calendar.
		/// </summary>
		/// <remarks>
		/// Printing is accomplished by painting a calendar in a rectangle 
		/// on a <see cref="Graphics"/> choosen. This method can also be used 
		/// to paint a calendar on a screen in a selected place or prepare 
		/// a bitmap with a calendar on it.<para/>
		/// Additionally, appearance calendar painted, can be controlled using 
		/// a <see cref="RootCalendar.PrintingMode"/> property. 
		/// It allows to paint only some elements such as frames, lines or strings. 
		/// Backgrounds are not painted. Therefore we get a simple printout saving  
		/// toner or ink.
		/// </remarks>
		/// <param name="graphics">A <see cref="Graphics"/> object a calendar 
		///		is printed on. If a method is called in the 
		///		<see cref="System.Drawing.Printing.PrintDocument.PrintPage"/> event, a parameter 
		///		<see cref="System.Drawing.Printing.PrintPageEventArgs.Graphics"/> 
		///		should be passed here.</param>
		/// <param name="rect">The rectangle a calendar is to be printed in.</param>
		public void Print(Graphics graphics, Rectangle rect) 
		{
			Matrix saveM = graphics.Transform;
			Region saveClip = graphics.Clip;
			try 
			{
				using (Region clip = saveClip.Clone())
				using (Matrix m = saveM.Clone()) 
				{
					clip.Intersect(rect);
					graphics.Clip = clip;

					m.Translate(rect.X, rect.Y);
					graphics.Transform = m;

					printInfo = new PrintInfo(rect);
					Resync(true);
					OnPaint(new PaintEventArgs(graphics, rect));
				}
			}
			finally 
			{
				printInfo = null;
				Resync(true);
				graphics.Transform = saveM;
				graphics.Clip = saveClip;
			}
		}

		internal void baseOnPaint(PaintEventArgs pe) 
		{
			if (!IsPrinting)
				base.OnPaint(pe);
		}

		internal bool paintBack 
		{
			get { return !IsPrinting || PrintingMode==PrintingMode.Standard; }
		}


		public virtual void Print(Graphics graphics)
		{
			
		}

		#endregion

		#region -- Custom Printing --
		/// <summary>
		/// Gets the rectangle for printing canvas.
		/// </summary>
		public virtual Rectangle PrintCanvasRectangle
		{
			get
			{
				return Rectangle.Empty;
			}
		}

		#endregion

		public virtual void Reload()
		{
			ReloadAppointments();
		}

	}
}
